#include "prisoner.h"
#include "ga.h"
#include "league.h"
#include <stdlib.h>
#include <sys/time.h>

extern struct timeval cur_time;
extern struct timeval begin_time;

GA::GA(Population &pro, Population &anti, unsigned int dur, int p, float m, float c, float r, float s)
:protagonist(pro), antagonist(anti)
{
	duration = dur;
	popSize = p;
	mutRate = m;
	crossRate = c;
	replaceRate = r;
	surviveRate = s;
	
	bestScore = -1;

	/** init incubator **/
	incubator = (Genome**) malloc(sizeof(Genome*)*int(replaceRate*popSize));
	for(int i = 0; i < replaceRate*popSize; i++)
	{
		incubator[i] = new Genome();
	}
}


bool GA::stop()
{
	gettimeofday(&cur_time,NULL);
	return (cur_time.tv_sec - begin_time.tv_sec >= duration);	
}


/** the comparison function **/
int _compareScore(const void* s1, const void* s2)
{
	float diff = (*(Genome*const*)s1)->score -	(*(Genome*const*)s2)->score;
	if(diff ==0)
		diff = (*(Genome*const*)s1)->rules.size()- (*(Genome*const*)s1)->rules.size();

	return diff > 0 ? -1 : (diff < 0 ? 1 : 0);
}



Genome* GA::run() {
	Genome *best = NULL;
	int generation = 1;

	int proSize = protagonist.size();
	int antiSize = antagonist.size();

	while(!stop()) {

		/** for each generation, run a league and evaluate **/
		/** run league **/
		League league(protagonist, antagonist);
		
		/** sort by score **/
		qsort(protagonist.raw, protagonist.size(), sizeof(Genome*), _compareScore);
		qsort(antagonist.raw, antagonist.size(), sizeof(Genome*), _compareScore);
	
		/** update best individual **/
		if((cur_time.tv_sec - begin_time.tv_sec >= duration/2) && (bestScore == -1 ||  bestScore > protagonist.raw[0]->score))
		{
			bestScore = protagonist.raw[0]->score;
			if(best != NULL)
				delete best;
			best = new Genome();
			*best = *protagonist.raw[0];
			best->print(stdout);
		}
		else if((cur_time.tv_sec - begin_time.tv_sec < duration/2)) {
			printf("====\n");
			protagonist.raw[0]->print(stdout);
		}
		//antagonist.raw[0]->print(stdout);


		/** do this twice: for protagonist, then antagonist **/
		/** protagonist **/
		/** select parents **/ 
		int replaced = 0;
		for(int i = 0; i < int(replaceRate*proSize); i++)
		{
			Genome& dad = protagonist.select();
			Genome& mom = protagonist.select();
			Genome& offspring = *incubator[i];
			if(flipCoin(crossRate)) {
				Genome::crossover(dad, mom, offspring);

				if(flipCoin(mutRate))
					Genome::mutate(offspring);
				replaced ++;
			}
		}
		/** replace with these new offsprings **/
		for(int i = 0; i < int(replaceRate*proSize); i++)
		{
			Genome *tmp = protagonist.raw[proSize-1-i];
			protagonist.raw[i-1-i] = incubator[i];
			incubator[i] = tmp;
		}

		/** antagonist **/		
		/** select parents **/ 
		replaced = 0;
		for(int i = 0; i < int(replaceRate*proSize); i++)
		{
			Genome& dad = antagonist.select();
			Genome& mom = antagonist.select();
			Genome& offspring = *incubator[i];
			if(flipCoin(crossRate)) {
				Genome::crossover(dad, mom, offspring);

				if(flipCoin(mutRate))
					Genome::mutate(offspring);
				replaced ++;
			}

		}
		/** replace with these new offsprings **/
		for(int i = 0; i < replaced; i++)
		{
			Genome *tmp = antagonist.raw[antiSize-1-i];
			antagonist.raw[antiSize-1-i] = incubator[i];
			incubator[i] = tmp;
		}

		generation ++;
	}
	return best;
}


